package com.example.sys.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.common.utils.R;
import com.example.common.entity.LoginLog;
import com.example.common.utils.Snowflake;
import com.example.common.utils.Util;
import com.example.sys.entity.Menus;
import com.example.sys.entity.User;
import com.example.sys.mapper.CommonMapper;
import com.example.sys.mapper.MenuMapper;
import com.example.sys.mapper.UserMapper;
import com.example.sys.service.CommonService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class CommonServiceImpl implements CommonService {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    private CommonMapper commonMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private MenuMapper menuMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R sendCode(String params) {
        Map<String, String> map = Util.transformParams(params);
        String phone = map.get("phone");
        int total = commonMapper.getSmsLog(phone);
        if(total < 5) {
            if(Util.sendCode(phone)) {
                return R.success("验证码发送成功");
            }
            return R.error("验证码发送失败");
        }
        return R.error("今日短信发送次数已上限");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R login(String params, HttpServletRequest request) {
        Map<String, String> mapParams = Util.transformParams(params);
        String type = mapParams.get("type");
        if("code".equals(type)) {
            return this.loginByCode(mapParams.get("phone"), mapParams.get("code"), request);
        }
        if("wx".equals(type)) {
            return this.wxLogin(mapParams.get("code"), request);
        }
        return this.loginByUserName(mapParams.get("username"), mapParams.get("password"), request, "username");
    }

    @Override
    public R getRoutes(String params) {
        String role = Util.transformParams(params).get("role");
        List<Menus> data = menuMapper.getMenuByRole(role);
        List<Menus> menus = Util.formatMenus(data);
        return R.success(menus);
    }

    /**
     * 验证码登陆
     * @param phone 手机号
     * @param code  验证码
     * @return  验证结果
     */
    private R loginByCode(String phone, String code, HttpServletRequest request) {
        String codeStr = redisTemplate.opsForValue().get(phone);
        if(StringUtils.isBlank(codeStr)) {
            return R.error("验证码已过期");
        }
        if(!codeStr.equals(code)) {
            return R.error("验证码错误");
        }
        User user = userMapper.loginByUsernameOrPhone(phone);
        // 删除验证码缓存
        redisTemplate.delete(phone);
        if(ObjectUtils.isEmpty(user)) {
            user.setId(Util.generateUID(11));
            user.setNickname("手机号用户" + phone);
            user.setUsername(phone);
            user.setPhone(phone);
            user.setRole("10");
            user.setPassword(Util.generateLettersAndNumbers(12));
            userMapper.insert(user);
        }
        return this.loginByUserName(user.getUsername(), user.getPassword(), request, "phone");
    }

    /**
     * 微信登陆
     * @param code 微信登陆CODE
     * @param request   请求头
     * @return  登陆结果
     */
    private R wxLogin(String code, HttpServletRequest request) {
        StringBuffer url = new StringBuffer("https://api.weixin.qq.com/sns/jscode2session");
        url.append("?appid=wx67266e52d984a669")
                .append("&secret=87f1fb60f186de9aa3bfd07966be7f8b")
                .append("&grant_type=authorization_code")
                .append("&js_code=")
                .append(code);
        String result = HttpUtil.get(String.valueOf(url));
        log.info("微信登陆结果：{}", result);
        Map<String, Object> rs = JSONObject.parseObject(result, Map.class);
        if(result.contains("errcode")) {
            return R.error("登陆失败");
        }
        String openid = rs.get("openid").toString();
        User user = userMapper.getOneByOpenId(openid);
        // 首次登陆
        if(ObjectUtils.isEmpty(user)) {
            user.setId(Util.generateUID(11));
            user.setNickname("小程序用户" + Util.generateLettersAndNumbers(6));
            user.setUsername(Util.generateLettersAndNumbers(8));
            user.setRole("10");
            user.setOpenId(openid);
            userMapper.insert(user);
        }
        return this.loginByUserName(user.getUsername(), user.getPassword(), request, "weixin");
    }

    /**
     * 用户名密码登陆
     * @param username 用户名
     * @param password  密码
     * @param request   请求头
     * @param type  登陆类型
     * @return  登陆结果
     */
    private R loginByUserName(String username, String password, HttpServletRequest request, String type) {
        Authentication authentication = null;
        try {
            authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(username, password));
        } catch (AuthenticationException e) {
            log.error("登陆失败：", e);
        }
        SecurityContextHolder.getContext().setAuthentication(authentication);
        Object principal = authentication.getPrincipal();
        User user = new User();
        BeanUtils.copyProperties(principal, user);
        user.setPassword("");
        user.setOpenId("");
        LoginLog loginLog = new LoginLog(String.valueOf(Snowflake.nextId()), user.getId(),
                request.getRemoteAddr(), request.getHeader("User-Agent"), type);
        commonMapper.loginLog(loginLog);
        return R.success("登陆成功", user);
    }
}
